﻿using System;
using System.Collections.Generic;
using UnityEngine;
using Yoozoo.Core.Common;
using Yoozoo.Gameplay.RTS;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace ImageEffects
{
#if UNITY_EDITOR
    [ExecuteInEditMode]
#endif
    public class LightController: 
        QualityListenerMono<LightController.LightControllerQualitySetting>
    {
        public static Dictionary<string, LightController> lightControllerMap = new Dictionary<string, LightController>(10);

        [Serializable]
        public class LightControllerQualitySetting : QualitySetting
        {
            public Light mainLight;
            public Light secondLight;
            public Color shadowColor = new Color(0.1f, 0.1f, 0.1f, 1.0f);
        }
        public bool openQualityListener = false;

        public List<LightElement> AllLightElements = new List<LightElement>();

        public string markLightString;
        
        private HashSet<int> gpuSkinningMaterials = new HashSet<int>();

        protected override void Awake()
        {
            base.Awake();

            foreach (var setting in QualitySettings)
            {
                setting.mainLight.enabled = false;
                setting.secondLight.enabled = false;

                //setting.mainLight.cullingMask = 0;
                //setting.secondLight.cullingMask = 0;
            }
            
            Add2ControllerMap();
        }

        public void Add2ControllerMap()
        {
            if (lightControllerMap == null)
            {
                lightControllerMap = new Dictionary<string, LightController>(10);
            }

            if (!string.IsNullOrEmpty(markLightString) && !lightControllerMap.ContainsKey(markLightString))
            {
                lightControllerMap[markLightString] = this;
            }
        }
        
        protected override void OnEnable()
        {
            if (openQualityListener)
            {
                base.OnEnable();
            }
        }

        protected override void OnDisable()
        {
            if (openQualityListener)
            {
                base.OnDisable();
            }
        }

        public override void ChangeQualitySetting(LightControllerQualitySetting qualitySetting)
        {
            CopyLight(qualitySetting.mainLight, Main_Light);
            CopyLight(qualitySetting.secondLight,Second_Light);
            Shadow_Color = qualitySetting.shadowColor;

            if (globalSetVector)
            {
                GlobalShaderSetting(true);
                return;
            }
            ShaderSetting(true);
        }
        
        private void CopyLight(Light original,Light destination)
        {
            destination.color = original.color;
            destination.renderMode = original.renderMode;
            destination.intensity = original.intensity;
            destination.shadows = original.shadows;
            destination.range = original.range;
            destination.type = original.type;
            destination.flare = original.flare;
            destination.bounceIntensity = original.bounceIntensity;
            destination.cullingMask = original.cullingMask;
            
            destination.transform.position = original.transform.position;
            destination.transform.rotation = original.transform.rotation;
        }
        

        /// <summary>
        /// 在编辑器模式下 是否只运行一次 表示只生效一次
        /// </summary>
        public bool m_isRunOnceInEditor = true;
        public Light Main_Light = null;
        public Transform Main_Light_Transform;
        public Light Second_Light = null;
        public Transform Second_Light_Transform;
        public Color Shadow_Color = new Color(0.1f, 0.1f, 0.1f, 1.0f);

        public bool frameUpdate = false;
        public bool globalSetVector = false;

        private Vector4 _mainLightColor;
        private Vector4 _mainLightPos;
        private Vector4 _secondLightColor;
        private Vector4 _secondLightPos;
        private Vector4 _ambienceColor;
        private Vector4 _globalShadowCol;
        //private static readonly int LightGlobalControl = Shader.PropertyToID("_LIGHTCONTROLGLOBAL");
        private static readonly int MainLightCol = Shader.PropertyToID("_MainLightCol");
        private static readonly int MainLightPos = Shader.PropertyToID("_MainLightPos");
        private static readonly int MainLightPosShadow = Shader.PropertyToID("_MainLightPosition");
        private static readonly int SecondLightCol = Shader.PropertyToID("_SecondLightCol");
        private static readonly int SecondLightPos = Shader.PropertyToID("_SecondLightPos");
        private static readonly int AmbienceColor = Shader.PropertyToID("_AmbienceColor");
        private static readonly int GlobalShadowCol = Shader.PropertyToID("_GlobalShadowCol");

        private static readonly int MainLightColGlobal = Shader.PropertyToID("_MainLightColGlobal");
        private static readonly int MainLightPosGlobal = Shader.PropertyToID("_MainLightPosGlobal");
        private static readonly int SecondLightColGlobal = Shader.PropertyToID("_SecondLightColGlobal");
        private static readonly int SecondLightPosGlobal = Shader.PropertyToID("_SecondLightPosGlobal");
        private static readonly int AmbienceColorGlobal = Shader.PropertyToID("_AmbienceColorGlobal");
        private static readonly int GlobalShadowColGlobal = Shader.PropertyToID("_GlobalShadowColGlobal");

        private void Start()
        {
            if (globalSetVector)
            {
                GlobalShaderSetting(true);
                return;
            }

            for (int i = 0; i < AllLightElements.Count; i++)
            {
                if (AllLightElements[i] == null)
                {
                    AllLightElements.RemoveAt(i);
                    i--;
                }
            }


            ShaderSetting(true);

            //if (Main_Light) Main_Light.cullingMask = 0;
            //if (Second_Light) Second_Light.cullingMask = 0;

        }



        
        private void LateUpdate()
        {
#if UNITY_EDITOR

            if (m_isRunOnceInEditor)
            {
                return;
            }
            else 
            {
                if (globalSetVector)
                {
                    GlobalShaderSetting(false);
                    return;
                }
                ShaderSetting(false);
            }
#else

            if(frameUpdate){
                if (globalSetVector)
                {
                    GlobalShaderSetting(false);
                    return;
                }
                ShaderSetting(false);
            }

#endif
        }

        public void SetMaterial(Material mat)
        {
            mat.SetVector(MainLightCol, _mainLightColor);
            mat.SetVector(MainLightPos, _mainLightPos);
            mat.SetVector(SecondLightCol, _secondLightColor);
            mat.SetVector(SecondLightPos, _secondLightPos);
        }


        public void GlobalShaderSetting(bool force)
        {
            if (Main_Light == null || Main_Light_Transform == null || Second_Light == null || Second_Light_Transform == null)
            {
                return;
            }

            Vector4 mainLightColor = Main_Light.color * Main_Light.intensity;
            Vector4 mainLightPos = Main_Light_Transform.forward;
            Vector4 secondLightColor = Second_Light.color * Second_Light.intensity;
            Vector4 secondLightPos = Second_Light_Transform.forward;
            Vector4 ambienceColor = RenderSettings.ambientLight;
            Vector4 globalShadowCol = Shadow_Color;

            if (mainLightColor == _mainLightColor &&
                mainLightPos == _mainLightPos &&
                secondLightColor == _secondLightColor &&
                secondLightPos == _secondLightPos &&
                ambienceColor == _ambienceColor &&
                globalShadowCol == _globalShadowCol)
            {

                if (!force)
                {
                    return;
                }
            }

            _mainLightColor = mainLightColor;
            _mainLightPos = mainLightPos;
            _secondLightColor = secondLightColor;
            _secondLightPos = secondLightPos;
            _ambienceColor = ambienceColor;
            _globalShadowCol = globalShadowCol;


            Shader.EnableKeyword("_GLOBALLIGHTCONTROLLER");
            Shader.SetGlobalVector(MainLightColGlobal, _mainLightColor);
            Shader.SetGlobalVector(MainLightPosGlobal, _mainLightPos);
            Shader.SetGlobalVector(SecondLightColGlobal, _secondLightColor);
            Shader.SetGlobalVector(SecondLightPosGlobal, _secondLightPos);
            //Shader.SetGlobalVector(AmbienceColorGlobal, _ambienceColor);
            Shader.SetGlobalVector(GlobalShadowColGlobal, _globalShadowCol);
            Shader.SetGlobalVector(MainLightPosShadow, _mainLightPos);

        }

        public void ShaderSetting(bool force)
        {
            if (Main_Light == null || Main_Light_Transform == null || Second_Light == null || Second_Light_Transform == null) 
            {
                return;
            }
            
            Vector4 mainLightColor = Main_Light.color * Main_Light.intensity;
            Vector4 mainLightPos = Main_Light_Transform.forward;
            Vector4 secondLightColor = Second_Light.color * Second_Light.intensity;
            Vector4 secondLightPos = Second_Light_Transform.forward;
            Vector4 ambienceColor = RenderSettings.ambientLight;
            Vector4 globalShadowCol = Shadow_Color;

            if (mainLightColor == _mainLightColor && 
                mainLightPos == _mainLightPos &&
                secondLightColor == _secondLightColor &&
                secondLightPos == _secondLightPos &&
                ambienceColor == _ambienceColor &&
                globalShadowCol == _globalShadowCol)
            {

                if (!force)
                {
                    return;
                }
            }

            _mainLightColor = mainLightColor;
            _mainLightPos = mainLightPos;
            _secondLightColor = secondLightColor;
            _secondLightPos = secondLightPos;
            _ambienceColor = ambienceColor;
            _globalShadowCol = globalShadowCol;


            if (cachedPropertyBlock == null)
            {
                cachedPropertyBlock = new MaterialPropertyBlock();
            }

            Shader.DisableKeyword("_GLOBALLIGHTCONTROLLER");
            cachedPropertyBlock.SetVector(MainLightCol, _mainLightColor);
            cachedPropertyBlock.SetVector(MainLightPos, _mainLightPos);
            cachedPropertyBlock.SetVector(SecondLightCol, _secondLightColor);
            cachedPropertyBlock.SetVector(SecondLightPos, _secondLightPos);
            cachedPropertyBlock.SetVector(AmbienceColor, _ambienceColor);
            cachedPropertyBlock.SetVector(GlobalShadowCol, _globalShadowCol);
            
            if (AllLightElements!=null)
            {
                foreach (var lightElement in AllLightElements)
                {
                    if (lightElement!=null)
                    {
                        SingleShaderSetting(lightElement);
                    }
                }
            }
            foreach(var configId in gpuSkinningMaterials)
            {
                SetGpuSkinningMaterial(configId);
            }
        }
        
        public static void AddGpuSkinningMaterial(string controlMark,int configId)
        {
            if (lightControllerMap.TryGetValue(controlMark, out var controller))
            {
                if (controller.gpuSkinningMaterials.Contains(configId))
                {
                    return;
                }
                controller.SetGpuSkinningMaterial(configId);
                controller.gpuSkinningMaterials.Add(configId);
            }
        }

        public static void UpdateForward(string controlMark,Vector3 forward)
        {
            if (lightControllerMap.TryGetValue(controlMark, out var controller))
            {
                controller.Main_Light_Transform.forward = forward;
                controller.ShaderSetting(false);
            }
        }

        private void SetGpuSkinningMaterial(int configId)
        {
            var mat = GPUInstancingManager.GetMainMaterial(configId);
            if (mat != null)
            {
                mat.SetVector(MainLightCol,_mainLightColor);
                mat.SetVector(MainLightPos, _mainLightPos);
                mat.SetVector(SecondLightCol, _secondLightColor);
                mat.SetVector(SecondLightPos, _secondLightPos);
            }
            mat = GPUInstancingManager.GetShadowMaterial(configId);
            if (mat != null)
            {
                mat.SetVector(MainLightPosShadow, _mainLightPos);
            }
        }

        public static void ClearGpuSkinningMaterial()
        {
            foreach (var controller in lightControllerMap.Values)
            {
                controller.gpuSkinningMaterials.Clear();
            }
        }
        
        private MaterialPropertyBlock cachedPropertyBlock { get; set; }

        
        // private Dictionary<LightElement, Dictionary<Material, MaterialPropertyBlock>> blockPool =
        //     new Dictionary<LightElement, Dictionary<Material, MaterialPropertyBlock>>();
        public void SingleShaderSetting(LightElement lightElement)
        {
#if UNITY_EDITOR
            if (lightElement != null)
            {
                if (lightElement.LightMaterials != null)
                {
                    lightElement.LightMaterials.Clear();
                }
                else
                {
                    lightElement.LightMaterials = new List<Material>(10);
                }
            }
#endif
            // if (!blockPool.TryGetValue(lightElement, out var pool))
            // {
            //     pool = new Dictionary<Material, MaterialPropertyBlock>();
            //     blockPool.Add(lightElement,pool);
            // }

            foreach (var lightRenderer in lightElement.LightRenderers)
            {
                if (lightRenderer == null)
                {
                    continue;
                }
#if UNITY_EDITOR
                foreach (var sharedMaterial in lightRenderer.sharedMaterials)
                {
                    if (sharedMaterial != null /*&& sharedMaterial.HasProperty("_MainLightCol")*/)
                    {
                        Material mat = sharedMaterial;
                        if (mat)
                        {
                            lightElement.LightMaterials.Add(mat);
                        }
                    }
                }
#endif        
                var propertyBlock = cachedPropertyBlock;
                if (lightElement is SurvivorLightElement survivorLightElement)
                {
                    var lightElementPropertyBlock = survivorLightElement.GetPropertyBlock();
                    if (lightElementPropertyBlock != null)
                    {
                        propertyBlock = lightElementPropertyBlock;
                        propertyBlock.SetVector(MainLightCol, _mainLightColor);
                        propertyBlock.SetVector(MainLightPos, _mainLightPos);
                        propertyBlock.SetVector(SecondLightCol, _secondLightColor);
                        propertyBlock.SetVector(SecondLightPos, _secondLightPos);
                        propertyBlock.SetVector(AmbienceColor, _ambienceColor);
                        propertyBlock.SetVector(GlobalShadowCol, _globalShadowCol);
                    }
                }
                lightRenderer.SetPropertyBlock(propertyBlock);
            }
        }

        public void AddLightElement(LightElement lightElement)
        {
            if (lightElement == null)
            {
                return;
            }
            
            if (AllLightElements.Contains(lightElement))
            {
                return;
            }
            AllLightElements.Add(lightElement);
            SingleShaderSetting(lightElement);
            
            lightElement.OnSetController(this);
            
        }

        public void RemoveLightingElement(LightElement lightElement)
        {
            if (lightElement == null)
            {
                return;
            }
            
            if (AllLightElements.Contains(lightElement))
            {
                AllLightElements.Remove(lightElement);
            }

            // if (blockPool.ContainsKey(lightElement))
            // {
            //     blockPool.Remove(lightElement);
            // }
        }


        private void OnDestroy()
        {
            if (AllLightElements!=null)
            {
                foreach (var lightElement in AllLightElements)
                {
                    if (lightElement)
                    {
                        lightElement.OnRemoved();
                    }
                }
                AllLightElements = null;
            }

            if (!string.IsNullOrEmpty(markLightString))
            {
                lightControllerMap.Remove(markLightString);
            }
            
        }


#if UNITY_EDITOR

        private void OnValidate()
        {
            if (globalSetVector)
            {
                GlobalShaderSetting(true);
                return;
            }

            ShaderSetting(true);
        }

#endif
    }
}
